import sys
import os
import json
import logging
from pathlib import Path
from jinja2 import Template, Environment, FileSystemLoader
from qtpy.QtWidgets import (
    QApplication, QWidget, QVBoxLayout, QTextEdit, QPushButton,
    QMessageBox, QLabel, QHBoxLayout
)
from qtpy.QtCore import Qt

from qtpy.QtCore import Qt, QThread, Signal as pyqtSignal, QEvent, QUrl, QTimer
from qtpy.QtWebEngineWidgets import QWebEngineView
from qtpy.QtWebEngineCore import QWebEngineSettings
from openai import OpenAI
import markdown

from config import config, Config
from logger import get_logger

# 获取日志记录器
logger = get_logger(__name__)

# 验证配置
if not config.is_valid:
    error_msg = config.get_error_message()
    logger.error(f"配置错误: {error_msg}")
    sys.exit(1)

class AIResponseThread(QThread):
    """处理AI响应的线程类"""
    chunk_received = pyqtSignal(str)  # 只发送新增的内容块
    error_occurred = pyqtSignal(str)
    
    def __init__(self, client, messages):
        super().__init__()
        self.client = client
        self.messages = messages
        self.logger = get_logger(__name__)
    
    def run(self):
        try:
            self.logger.debug(f"开始生成响应，使用模型: {config.model}")
                            
            response = self.client.chat.completions.create(
                model=config.model,
                messages=self.messages,
                stream=True,  # 启用流式响应
            )
            
            for chunk in response:
                if chunk.choices[0].delta.content is not None:
                    content = chunk.choices[0].delta.content
                    # 只有当内容不为空时才发送
                    if content.strip():
                        self.chunk_received.emit(content)
                        self.logger.debug(f"接收到响应块: {content}")
            
        except Exception as e:
            error_msg = f"生成响应时发生错误: {str(e)}"
            self.logger.error(error_msg, exc_info=True)
            self.error_occurred.emit(error_msg)

class ChatGPTWidget(QWidget):
    def __init__(self):
        super().__init__()
        # 初始化日志记录器
        self.logger = get_logger(__name__)
        
        # 初始化所有实例变量
        self.client = None
        self.response_thread = None
        self.current_response = ""  # 存储当前完整响应
        self.current_user_input = ""  # 存储当前用户输入
        self.response_display = None
        self.input_text = None
        self.send_button = None
        self.conversation = []  # 存储对话历史
        
        # 创建防抖动定时器
        self.update_timer = QTimer()
        self.update_timer.setSingleShot(True)  # 设置为单次触发
        self.update_timer.timeout.connect(self._do_update_web_view)
        
        # 加载HTML模板
        self.load_html_template()
        
        # 初始化界面和功能
        self.initUI()
        self.setupShortcuts()
        self.initClient()
        
        # 加载历史对话
        self.load_chat_history()
        
    def load_chat_history(self):
        """从文件加载聊天历史"""
        try:
            history_file = Path(config.history_dir) / "chat_history.json"
            
            if not history_file.exists():
                self.logger.info("未找到历史记录文件，将使用空对话历史")
                return
            
            self.logger.debug(f"正在加载聊天历史: {history_file}")
            with open(history_file, 'r', encoding='utf-8') as f:
                self.conversation = json.load(f)
            self.logger.info("聊天历史加载成功")
            
            # 更新显示
            self.update_web_view()
            
        except Exception as e:
            error_msg = f"加载聊天历史失败: {str(e)}"
            self.logger.error(error_msg, exc_info=True)
            QMessageBox.warning(self, "警告", error_msg)
            # 如果加载失败，使用空对话历史
            self.conversation = []
            
    def save_chat_history(self):
        """保存聊天历史到文件"""
        try:
            # 确保历史记录目录存在
            history_dir = Path(config.history_dir)
            history_dir.mkdir(parents=True, exist_ok=True)
            
            # 构建历史文件路径
            history_file = history_dir / "chat_history.json"
            
            # 只保留最近的消息
            if len(self.conversation) > config.max_history:
                self.conversation = self.conversation[-config.max_history:]
            
            # 保存聊天历史
            self.logger.debug(f"正在保存聊天历史到: {history_file}")
            with open(history_file, 'w', encoding='utf-8') as f:
                json.dump(self.conversation, f, ensure_ascii=False, indent=2)
            self.logger.info("聊天历史保存成功")
            
        except Exception as e:
            error_msg = f"保存聊天历史失败: {str(e)}"
            self.logger.error(error_msg, exc_info=True)
            QMessageBox.warning(self, "警告", error_msg)
    
    # HTML模板从外部文件加载
    HTML_TEMPLATE = ""
    
    def load_html_template(self):
        """从外部文件加载HTML模板"""
        try:
            template_dir = os.path.dirname(__file__)
            self.jinja_env = Environment(loader=FileSystemLoader(template_dir))
            self.template = self.jinja_env.get_template('template.html')
            self.logger.info("成功加载HTML模板")
        except Exception as e:
            error_msg = f"加载HTML模板失败: {str(e)}"
            self.logger.error(error_msg, exc_info=True)
            QMessageBox.warning(self, "警告", error_msg)
            # 如果加载失败，使用简单的备用模板
            self.template = Template("""<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"></head>
<body>{{ content }}</body>
</html>""")

    def initUI(self):
        # 设置窗口标题和大小
        self.setWindowTitle(config.app_title)
        self.setGeometry(100, 100, config.window_width, config.window_height)
        
        # 创建垂直布局
        layout = QVBoxLayout(self)
        layout.setContentsMargins(10, 10, 10, 10)
        layout.setSpacing(10)
        
        self.logger.debug("初始化UI布局")
        
        # 响应显示区域 - 使用QWebEngineView替代QTextEdit
        self.response_display = QWebEngineView()
        self.response_display.setMinimumHeight(400)
        
        # 设置WebEngine设置
        settings = self.response_display.settings()
        settings.setAttribute(QWebEngineSettings.JavascriptEnabled, True)
        settings.setAttribute(QWebEngineSettings.LocalContentCanAccessRemoteUrls, True)
        
        # 初始化显示空内容
        self.update_web_view("")
        
        # 聊天输入框
        self.input_text = QTextEdit()
        self.input_text.setPlaceholderText('请输入你的问题...')
        self.input_text.setMaximumHeight(100)
        
        # 状态标签
        self.status_label = QLabel('')
        self.status_label.setStyleSheet("""
            QLabel {
                color: #666;
                font-style: italic;
                padding: 5px;
            }
        """)
        
        # 发送按钮
        self.send_button = QPushButton('发送')
        self.send_button.clicked.connect(self.handle_send)
        self.send_button.setStyleSheet("""
            QPushButton {
                background-color: #007bff;
                color: white;
                border: none;
                border-radius: 4px;
                padding: 8px 16px;
            }
            QPushButton:hover {
                background-color: #0056b3;
            }
            QPushButton:pressed {
                background-color: #004085;
            }
            QPushButton:disabled {
                background-color: #6c757d;
            }
        """)
        
        # 创建底部布局
        bottom_layout = QHBoxLayout()
        bottom_layout.addWidget(self.status_label, stretch=1)
        bottom_layout.addWidget(self.send_button)
        
        # 添加组件到布局
        layout.addWidget(self.response_display)
        layout.addWidget(self.input_text)
        layout.addLayout(bottom_layout)
    
    def setupShortcuts(self):
        """设置快捷键"""
        self.input_text.installEventFilter(self)
    
    def eventFilter(self, obj, event):
        """事件过滤器，处理快捷键"""
        if obj == self.input_text and event.type() == QEvent.KeyPress:
            if event.key() == Qt.Key_Return:
                if event.modifiers() == Qt.ControlModifier:
                    self.handle_send()
                    return True
                elif not event.modifiers():
                    return False
        return super().eventFilter(obj, event)
    
    def validate_config(self):
        """验证配置是否有效"""
        required_fields = ['api_key', 'api_base', 'model']
        missing_fields = [field for field in required_fields if not getattr(config, field, None)]
        
        if missing_fields:
            error_msg = f"配置缺少必要字段: {', '.join(missing_fields)}"
            self.logger.error(error_msg)
            QMessageBox.critical(self, "配置错误", error_msg)
            return False
            
        return True

    def initClient(self):
        """初始化OpenAI客户端"""
        try:
            self.logger.info("正在初始化API客户端...")
            
            # 验证配置
            if not self.validate_config():
                sys.exit(1)
                
            # 创建客户端
            self.client = OpenAI(
                base_url=config.api_base,
                api_key=config.api_key
            )
            
            # 测试API连接
            self.client.models.list()
            self.logger.info("API客户端初始化成功，已验证API连接")
            
        except Exception as e:
            error_msg = f"初始化API客户端失败：{str(e)}"
            self.logger.error(error_msg, exc_info=True)
            QMessageBox.critical(self, "错误", error_msg)
            sys.exit(1)
        
    def handle_send(self):
        """处理发送消息"""
        user_input = self.input_text.toPlainText().strip()
        if not user_input:
            return
        
        if not self.client:
            error_msg = "API客户端未初始化，请检查配置。"
            self.logger.error(error_msg)
            self.handle_error(error_msg)
            return
        
        # 重新加载配置并初始化客户端
        try:
            from config import reload_config, config
            new_config = reload_config()
            self.logger.debug(f"重新加载配置，当前模型: {new_config.model}")
            
            # 重新初始化客户端
            self.client = OpenAI(
                base_url=new_config.api_base,
                api_key=new_config.api_key
            )
            self.logger.debug("已使用新配置重新初始化客户端")
        except Exception as e:
            self.logger.warning(f"重新加载配置和初始化客户端时出错: {str(e)}")
            return
        
        self.logger.info("处理新的用户消息")
        
        # 保存用户输入用于显示
        self.current_user_input = user_input
        
        # 清空输入框并禁用发送按钮
        self.input_text.clear()
        self.send_button.setEnabled(False)
        
        # 更新状态标签
        self.status_label.setText("正在生成回答...")
        self.status_label.setStyleSheet("color: #007bff; font-style: italic;")
        
        # 重置当前响应
        self.current_response = ""
        
        # 构建消息历史
        messages = [{"role": "system", "content": config.system_prompt}]
        # 添加历史对话（如果配置允许）
        if config.use_history:
            messages.extend(self.conversation[-config.max_context:])
        # 添加当前用户消息
        messages.append({"role": "user", "content": user_input})
        
        # 添加用户消息到对话历史并更新显示
        self.conversation.append({
            "role": "user",
            "content": user_input
        })
        self.update_web_view()
        
        # 创建并启动响应线程
        if self.response_thread and self.response_thread.isRunning():
            self.logger.debug("终止现有的响应线程")
            self.response_thread.terminate()
            self.response_thread.wait()
            
        self.logger.debug("创建新的响应线程")
        self.response_thread = AIResponseThread(self.client, messages)
        self.response_thread.chunk_received.connect(self.handle_chunk)
        self.response_thread.error_occurred.connect(self.handle_error)
        self.response_thread.finished.connect(self.on_response_complete)
        self.response_thread.start()
        
        # 保存聊天历史
        self.save_chat_history()
    
    def update_web_view(self, content=None):
        """触发WebView内容更新
        
        Args:
            content: 可选的内容字符串，如果提供则直接使用，否则使用对话历史
        """
        self._cached_content = content  # 缓存要更新的内容
        self.update_timer.stop()  # 停止之前的定时器
        self.update_timer.start(200)  # 200ms 后更新UI
    
    def _do_update_web_view(self):
        """实际执行WebView更新的方法"""
        try:
            # 将对话内容转换为HTML格式
            html_content = []
            
            # 如果有缓存的特定内容，直接使用它
            if hasattr(self, '_cached_content') and self._cached_content is not None:
                html_content.append(self._cached_content)
                self._cached_content = None  # 清除缓存
            else:
                # 否则使用对话历史生成内容
                for message in self.conversation:
                    role = message["role"]
                    msg_content = message["content"]
                    
                    if not msg_content.strip():
                        continue
                    
                    # 将Markdown转换为HTML
                    html = markdown.markdown(msg_content, extensions=['fenced_code', 'tables'])
                    
                    # 根据角色添加不同的样式
                    if role == "user":
                        html_content.append(f'<div class="user-message"><strong>用户:</strong><br>{html}</div>')
                    else:
                        html_content.append(f'<div class="assistant-message"><strong>助手:</strong><br>{html}</div>')
            
            # 使用join连接HTML内容，这比字符串拼接更高效
            final_content = '\n'.join(html_content)
            
            # 使用Jinja2模板渲染内容
            full_html = self.template.render(content=final_content)
            
            # 更新WebView
            self.response_display.setHtml(full_html)
            
            # 滚动到底部
            self.response_display.page().runJavaScript("window.scrollTo(0, document.body.scrollHeight);")
            
        except Exception as e:
            self.logger.error(f"更新WebView时发生错误: {str(e)}", exc_info=True)

    def handle_chunk(self, chunk):
        """处理接收到的响应块"""
        # 添加新的响应块到当前响应
        self.current_response += chunk
        
        # 如果是第一个响应块，添加新的助手消息到对话历史
        if len(self.current_response) == len(chunk):
            self.conversation.append({
                "role": "assistant",
                "content": ""
            })
            # 更新状态标签为"正在接收响应..."
            self.status_label.setText("正在接收响应...")
            self.status_label.setStyleSheet("color: #28a745; font-style: italic;")
        
        # 更新最后一条助手消息
        if self.conversation and self.conversation[-1]["role"] == "assistant":
            self.conversation[-1]["content"] = self.current_response
            # 使用防抖动定时器更新UI
            self.update_timer.stop()  # 停止之前的定时器
            self.update_timer.start(200)  # 200ms 后更新UI
    
    def handle_error(self, error_message):
        """处理错误"""
        self.logger.error(f"发生错误: {error_message}")
        
        # 添加错误消息到对话历史
        self.conversation.append({
            "role": "assistant",
            "content": f"❌ **错误**: {error_message}"
        })
        
        # 更新WebView显示
        self.update_web_view()
        self.send_button.setEnabled(True)
        
        # 保存包含错误信息的对话历史
        self.save_chat_history()
    
    def on_response_complete(self):
        """响应完成后的处理"""
        self.logger.info("响应生成完成")
        
        # 更新状态标签
        self.status_label.setText("回答完成")
        self.status_label.setStyleSheet("color: #28a745; font-style: italic;")
        
        # 设置定时器，3秒后清除状态标签
        QTimer.singleShot(3000, lambda: self.status_label.setText(""))
        
        self.send_button.setEnabled(True)
        
        # 保存完整的对话历史
        self.save_chat_history()
        
        if self.response_thread:
            self.response_thread.deleteLater()
            self.response_thread = None
            self.logger.debug("清理响应线程")
    
    def closeEvent(self, event):
        """窗口关闭事件处理"""
        self.logger.info("应用程序正在关闭")
        if self.response_thread and self.response_thread.isRunning():
            self.logger.debug("终止运行中的响应线程")
            self.response_thread.terminate()
            self.response_thread.wait()
        
        # 保存最终的对话历史
        self.save_chat_history()
        self.logger.info("应用程序关闭完成")
        event.accept()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    chat_app = ChatGPTWidget()
    chat_app.show()
    sys.exit(app.exec_())